package client

import (
	"fmt"
	uuid "github.com/satori/go.uuid"
	"net"
	"os"
	"sync/atomic"
	"time"
	"tio-proxy-agent/core"
	"tio-proxy-agent/log"
)

type GioClientForward struct {
	config          core.GioConfig
	ReconnectConfig *core.ReconnectConfig
	*core.ChannelContext
	retryCount int32
	BsId       string
	isInitOk   chan bool
	goClose    chan bool
	name       string
}

func NewGioClientForward(config core.GioConfig, reconnect *core.ReconnectConfig, bsId string) *GioClientForward {
	gioClientForward := new(GioClientForward)
	gioClientForward.config = config
	gioClientForward.ReconnectConfig = reconnect
	gioClientForward.retryCount = 0
	gioClientForward.BsId = bsId
	gioClientForward.isInitOk = make(chan bool)
	gioClientForward.goClose = make(chan bool)
	gioClientForward.name = uuid.NewV4().String()
	return gioClientForward
}

func (gioClientForward *GioClientForward) Connect(ip string, port int) {
	ctx := core.NewChannelContext(ip, port, gioClientForward.config)
	if gioClientForward.ChannelContext != nil {
		close(gioClientForward.ChannelContext.NewReceivedByteBuffer)
	}
	gioClientForward.ChannelContext = ctx

	conn, err := net.Dial("tcp", ctx.BindIp+":"+fmt.Sprintf("%d", ctx.BindPort))
	if err != nil {
		gioClientForward.reconnect()
	}
	defer func(conn net.Conn) {
		err := conn.Close()
		if err != nil {
			close(gioClientForward.NewReceivedByteBuffer)
			close(gioClientForward.SendData)
			log.SugarLogger.Error("GioClientForward conn close异常:", err)
			os.Exit(1)
		}
	}(conn)

	gioClientForward.Set("conn", conn)
	gioClientForward.config.GetGioListener().OnAfterConnected(ctx)
	ChannelManage[gioClientForward.BsId] = gioClientForward.ChannelContext

	go func() {
		for {
			byteArray := make([]byte, 20480)
			n, err := conn.Read(byteArray)
			if err != nil {
				close(gioClientForward.NewReceivedByteBuffer)
				close(gioClientForward.SendData)
				gioClientForward.goClose <- true
				return
			}
			gioClientForward.NewReceivedByteBuffer <- byteArray[:n]
		}
	}()

	defer func() { <-gioClientForward.goClose }()

	gioClientForward.isInitOk <- true
}

func (gioClientForward *GioClientForward) reconnect() {
	if gioClientForward.ReconnectConfig != nil {
		ctx := gioClientForward.ChannelContext

		if gioClientForward.ReconnectConfig.RetryCount >= gioClientForward.retryCount {
			time.Sleep(time.Duration(gioClientForward.ReconnectConfig.Interval) * time.Millisecond)
			atomic.AddInt32(&gioClientForward.retryCount, 1)
			gioClientForward.Connect(ctx.BindIp, ctx.BindPort)
		}
	}
}
